#pragma once

#include "Mocker.h"

#define REGISTER(Function__) \
	Mocker::Instance().RegisterFunction(&Function__); \
	Mocker::Instance().SetFunctionName(#Function__);

#define REGISTER_STUB(Function__, ...) \
	Mocker::Instance().RegisterFunction(&Function__); \
	Mocker::Instance().SetFunctionName(#Function__); \
	Mocker::Instance().DefaultStub(__VA_ARGS__);

#define REGISTER_THROW(Function__, ...) \
	Mocker::Instance().RegisterFunction(&Function__); \
	Mocker::Instance().SetFunctionName(#Function__); \
	Mocker::Instance().DefaultThrowException(__VA_ARGS__);

#define REGISTER_OVERLOADED(Function__, Type__) \
	Mocker::Instance().RegisterFunction((Type__)&Function__); \
	Mocker::Instance().SetFunctionName(#Function__);

#define REGISTER_OVERLOADED_STUB(Function__, Type__, ...) \
	Mocker::Instance().RegisterFunction((Type__)&Function__); \
	Mocker::Instance().SetFunctionName(#Function__); \
	Mocker::Instance().DefaultStub(__VA_ARGS__);

#define REGISTER_OVERLOADED_THROW(Function__, Type__, ...) \
	Mocker::Instance().RegisterFunction((Type__)&Function__); \
	Mocker::Instance().SetFunctionName(#Function__); \
	Mocker::Instance().DefaultThrowException(__VA_ARGS__);

#define RECORD CHECK(true); for(Mocker::Instance().Record(); Mocker::Instance().IsRecording(); Mocker::Instance().Replay())

namespace MockItNow
{
	struct ExpectHelper
	{
		ExpectHelper& Ignore(int arguments)
		{
			Mocker::Instance().Ignore(arguments);
			return *this;
		}

		ExpectHelper& CallOriginal()
		{
			Mocker::Instance().CallOriginal();
			return *this;
		}

		ExpectHelper& FailWhenCalled()
		{
			Mocker::Instance().FailWhenCalled();
			return *this;
		}

		template<typename T>
		ExpectHelper& ReturnArgument(int arguments, T returnValue)
		{
			Mocker::Instance().Return(arguments, returnValue);
			return *this;
		}

		template<typename T>
		ExpectHelper& CompareArgument(int arguments, typename TypeInfo<T>::CompareFunction compareFunction)
		{
			Mocker::Instance().SetCompareFunction<T>(arguments, compareFunction);
			return *this;
		}
	};

	template<typename T>
	struct ExpectReturnHelper : public ExpectHelper
	{
		ExpectReturnHelper& Return(T returnValue)
		{
			Mocker::Instance().Return(returnValue);
			return *this;
		}

		ExpectReturnHelper& ReturnAlways(T returnValue)
		{
			Mocker::Instance().ReturnAlways(returnValue);
			return *this;
		}
	};

	template<typename T>
	struct ExpectThrowHelper : public ExpectHelper
	{
		ExpectThrowHelper& Throw(T exceptionValue)
		{
			Mocker::Instance().ThrowException(exceptionValue);
			return *this;
		}
	};

	template <typename T>
	ExpectReturnHelper<T> CreateExpectReturnHelper(T)
	{
		return ExpectReturnHelper<T>();
	}

	template <typename T>
	ExpectThrowHelper<T> CreateExpectThrowHelper(T)
	{
		return ExpectThrowHelper<T>();
	}
}

#define EXPECT(FunctionCall__) \
	FunctionCall__; \
	ExpectHelper()

#define EXPECT_RETURN(FunctionCall__, ReturnValue__) \
	CreateExpectReturnHelper(FunctionCall__).Return(ReturnValue__)

#define EXPECT_RETURN_ALWAYS(FunctionCall__, ReturnValue__) \
	CreateExpectReturnHelper(FunctionCall__).ReturnAlways(ReturnValue__)

#define EXPECT_THROW(FunctionCall__, ReturnValue__) \
	FunctionCall__; \
	CreateExpectThrowHelper(ReturnValue__).Throw(ReturnValue__)

#define FAIL_WHEN_CALLED(FunctionCall__) \
	FunctionCall__; \
	ExpectHelper().FailWhenCalled()

using namespace MockItNow;
